package com.create.myworld.worldthings;

import com.create.myworld.annotations.NotDo;
import com.create.myworld.enums.Sex;
import com.create.myworld.action.Action;
import com.create.myworld.living.people.AncestorPeople;
import com.create.myworld.objects.derive.substance.Living;
import com.create.myworld.objects.derive.substance.SubObject;
import com.create.myworld.objects.derive.substance.Work;
import com.create.myworld.sse.NormalSSE;
import com.create.myworld.util.RabbitMQUtil;
import com.create.myworld.util.TimeUtil;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zcw
 * 世界万物
 */
@Component
public class WorldThings {

    /**
     * Spring容器
     */
    public static ApplicationContext applicationContext;

    /**
     * 世界上所有生物
     */
    private static Map<String, Living> allLiving = new ConcurrentHashMap();

    /**
     * 世界上所有物体
     */
    private static List<SubObject> allObject = new ArrayList();

    /**
     * 世界上所有工作
     */
    private static List<Work> allWorks = new ArrayList();

    /**
     * 生物的所有行为
     */
    private static Map<Class, List<Method>> actionMap = new ConcurrentHashMap<>();

    /**
     * 不被执行的行为
     */
    private static String[] objectMethods = new String[]{"clone", "equals", "finalize", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait"};
    private static List<String> objectMethodList = Arrays.asList(objectMethods);

    private static Method[] concat(Method[] a, Method[] b) {
        Method[] c = new Method[a.length + b.length];
        System.arraycopy(a, 0, c, 0, a.length);
        System.arraycopy(b, 0, c, a.length, b.length);
        return c;
    }

    /**
     * 初始化世界
     */
    public static void init() {
        RabbitMQUtil.rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
        RabbitMQUtil.pushNormal("Spring Bean获取完成,准备初始化生物行为...");
        RabbitMQUtil.pushNormal("开始初始化生物行为");
        for (Map.Entry<String, Living> entry : allLiving.entrySet()) {
            Living living = entry.getValue();
            Class livingClass = living.getMyself();
            if (!actionMap.containsKey(livingClass)) {
                Method[] methods = concat(Action.class.getDeclaredMethods(), livingClass.getMethods());
                List<Method> actionList = Arrays.asList(methods);
                actionMap.put(livingClass, actionList);
            }
        }
        RabbitMQUtil.pushNormal("生物行为初始化完成");
    }

    /**
     * 注册生物
     *
     * @param livings
     */
    public static void insertLiving(Living... livings) {
        for (Living living : livings) {
            if (RabbitMQUtil.rabbitTemplate != null) {
                RabbitMQUtil.pushNormal(living.getName() + "注入世界,基础属性如下:");
            }
            living.introduce();
            allLiving.put(living.getId(), living);
        }
    }

    /**
     * 注册物体
     *
     * @param subObjects
     */
    public static void insertObject(SubObject... subObjects) {
        for (SubObject subObject : subObjects) {
            if (RabbitMQUtil.rabbitTemplate != null) {
                RabbitMQUtil.pushNormal(subObject.getName() + "注入世界");
            } else {
                NormalSSE.contentList.add(subObject.getName() + "注入世界");
            }
            allObject.add(subObject);
        }
    }

    public static void insertWork(Work... works) {
        for (Work work : works) {
            if (RabbitMQUtil.rabbitTemplate != null) {
                RabbitMQUtil.pushNormal(work.getName() + "工作注入世界");
            } else {
                NormalSSE.contentList.add(work.getName() + "工作注入世界");
            }
            allWorks.add(work);
        }
    }

    /**
     * 检测是否死亡
     *
     * @param living
     * @return
     */
    public static boolean checkDie(Living living) {
        return TimeUtil.getSeconds() >= (living.getStartTime() + living.getLiveTime());
    }

    /**
     * 控制死亡
     */
    @Scheduled(cron = "*/1 * * * * ?")
    public void controlDie() {
        for (Map.Entry<String, Living> entry : allLiving.entrySet()) {
            Living living = entry.getValue();
            if (checkDie(living)) {
                allLiving.remove(living.getId());
                living.die();
            }
        }
    }

    /**
     * 随机行为
     */
    @Scheduled(cron = "*/2 * * * * ?")
    public void doSomething() {
        for (Map.Entry<String, Living> entry : allLiving.entrySet()) {
            Living living = entry.getValue();
            if (living.getMyself() == AncestorPeople.class) {
                AncestorPeople people = (AncestorPeople) living;
                if (people.workRound > 0) {
                    RabbitMQUtil.pushNormal(people.getName() + "正在打工...");
                    if (people.workRound == 1) {
                        RabbitMQUtil.pushNormal(people.getName() + "工作结束,获得酬劳" + people.canGetCoin + "金币");
                    }
                    people.workRound--;
                    continue;
                }
                /**
                 * 结算工钱
                 */
                people.coin += people.canGetCoin;
                /**
                 * 工钱清零
                 */
                people.canGetCoin = 0L;
            }
            living.status = 1;
            List<Method> actionList = actionMap.get(living.getMyself());
            while (true) {
                int random = (int) (Math.random() * actionList.size());
                Method method = actionList.get(random);
                if (method.isAnnotationPresent(NotDo.class)) {
                    continue;
                }
                String methodName = method.getName();
                if (objectMethodList.contains(methodName)) {
                    continue;
                }
                try {
                    method.invoke(living, null);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }

    /**
     * 随机分发物品给人类
     */
    public static String giveObject(AncestorPeople people) {
        int random = (int) (Math.random() * allObject.size());
        SubObject object = allObject.get(random);
        people.subObjectList.add(object);
        return object.getName();
    }

    /**
     * 获取同种族异性的生物
     *
     * @param livingClass
     * @return
     */
    public static Living getSameClassLiving(Class livingClass, Sex sex) {
        sex = sex.getSex().equals(Sex.Male.getSex()) ? Sex.Female : Sex.Male;
        for (Map.Entry<String, Living> entry : allLiving.entrySet()) {
            Living living = entry.getValue();
            if (living.getMate() == null && living.getSex().getSex().equals(sex.getSex())) {
                if (living.getMyself() == livingClass) {
                    return living;
                }
            }
        }
        return null;
    }

    /**
     * 随机获取同种族生物
     *
     * @param livingClass
     * @return
     */
    public static <T extends Living> T getSameClassLiving(Class<T> livingClass, Living me) {
        for (Map.Entry<String, Living> entry : allLiving.entrySet()) {
            Living living = entry.getValue();
            if (living.getMyself() == livingClass) {
                if (!living.getId().equals(me.getId())) {
                    return (T) living;
                }
            }
        }
        return null;
    }

    /**
     * 随机获取指定类型物品
     */
    public static SubObject getSubObject(Class objectClass) {
        for (SubObject subObject : allObject) {
            if (subObject.objectClass == objectClass) {
                return subObject;
            }
        }
        return null;
    }


    /**
     * 随机获取工作
     */
    public static Work getWork() {
        int random = (int) (Math.random() * allWorks.size());
        return allWorks.get(random);
    }
}
